#include "Avalon/Network/TcpSocketWriter.hpp"
#include <boost/bind.hpp>
#include "Avalon/IO/Buffer.hpp"
#include "Avalon/Network/SocketException.hpp"
#include "Avalon/Network/SocketType.hpp"
#include "Avalon/Network/TcpSocketConnection.hpp"

using namespace Avalon;
using namespace Avalon::IO;
using namespace Avalon::Network;
using namespace Avalon::Network::Detail;
using namespace Avalon::Threading;
using namespace boost;
using namespace boost::asio;
using namespace boost::asio::ip;
using namespace boost::posix_time;
using namespace std;

TcpSocketWriter::~TcpSocketWriter() {}

Async<void>::Ptr TcpSocketWriter::Write(const void* data, int size) {
  Buffer dataCopy;
  dataCopy.Append(data, size);
  return Write(dataCopy);
}

Async<void>::Ptr TcpSocketWriter::Write(const Buffer& data) {
  boost::lock_guard<boost::recursive_mutex> lock(m_mutex);
  AsyncOwner<void>::Ptr writeResult = AsyncOwner<void>::Create();
  m_writeResult = writeResult;
  async_write(m_socket->m_socket, buffer(data.GetData(), data.GetSize()),
    bind(&TcpSocketWriter::HandleWrite, shared_from_this(), data,
    placeholders::error, placeholders::bytes_transferred));
  return writeResult;
}

TcpSocketWriter::TcpSocketWriter(const boost::shared_ptr<SocketType>& socket)
    : m_socket(socket) {}

void TcpSocketWriter::Abort() {
  boost::lock_guard<boost::recursive_mutex> lock(m_mutex);
  if(m_writeResult == NULL) {
    return;
  }
  AsyncOwner<void>::Ptr writeResult = m_writeResult;
  m_writeResult.reset();
  writeResult->SetException(IOException("Connection closed."));
}

void TcpSocketWriter::HandleWrite(const Buffer& data,
    const boost::system::error_code& error, size_t size) {
  boost::lock_guard<boost::recursive_mutex> lock(m_mutex);
  if(m_writeResult == NULL) {
    return;
  }
  AsyncOwner<void>::Ptr writeResult = m_writeResult;
  m_writeResult.reset();
  if(error) {
    writeResult->SetException(SocketException(error.value(),
      error.message().c_str()));
  } else {
    writeResult->SetComplete();
  }
}
